package com.cyj.dream.gateway.filter;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.http.HttpUtil;
import com.cyj.dream.core.constant.Constant;
import com.cyj.dream.core.constant.SecurityConstants;
import com.cyj.dream.core.exception.BizException;
import com.cyj.dream.core.util.IsEmptyUtils;
import com.cyj.dream.core.util.codec.Base64Utils;
import com.cyj.dream.core.util.nosql.RedisUtil;
import com.cyj.dream.core.util.rsa.RSAUtils;
import com.cyj.dream.gateway.config.GatewayConfigProperties;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.publisher.Mono;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Map;

/**
 * @Description: 密码解密过滤器
 * @BelongsProject: DreamChardonnay
 * @BelongsPackage: com.cyj.dream.gateway.filter
 * @Author: ChenYongJia
 * @CreateTime: 2021-09-27 13:02
 * @Email: chen87647213@163.com
 * @Version: 1.0
 */
@Slf4j
@RequiredArgsConstructor
public class PasswordDecoderFilter extends AbstractGatewayFilterFactory {

    private static final String PASSWORD = "password";

    private static final String QRCODE = "QRCode";

    private static final String KEY_ALGORITHM = "AES";

    private final GatewayConfigProperties configProperties;

    @Autowired
    private RedisUtil redisUtil;

    /**
     * 解密
     *
     * @param data 数据
     * @param pass 密码
     * @return * @return java.lang.String
     * @author ChenYongJia
     * @date 13:37 2021/10/26
     */
    private static String decryptAES(String data, String pass) {
        AES aes = new AES(Mode.CBC, Padding.NoPadding, new SecretKeySpec(pass.getBytes(), KEY_ALGORITHM),
                new IvParameterSpec(pass.getBytes()));
        byte[] result = aes.decrypt(Base64.decode(data.getBytes(StandardCharsets.UTF_8)));
        return new String(result, StandardCharsets.UTF_8);
    }

    /**
     * AES加密字符串
     *
     * @param content  需要被加密的字符串
     * @param password 加密需要的密码
     * @return 密文
     */
    public static byte[] encrypt(String content, String password) {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");// 创建AES的Key生产者

            kgen.init(128, new SecureRandom(password.getBytes()));// 利用用户密码作为随机数初始化出
            // 128位的key生产者
            //加密没关系，SecureRandom是生成安全随机数序列，password.getBytes()是种子，只要种子相同，序列就一样，所以解密只要有password就行

            SecretKey secretKey = kgen.generateKey();// 根据用户密码，生成一个密钥

            byte[] enCodeFormat = secretKey.getEncoded();// 返回基本编码格式的密钥，如果此密钥不支持编码，则返回
            // null。

            SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");// 转换为AES专用密钥

            Cipher cipher = Cipher.getInstance("AES");// 创建密码器

            byte[] byteContent = content.getBytes("utf-8");

            cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化为加密模式的密码器

            byte[] result = cipher.doFinal(byteContent);// 加密

            return result;

        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public GatewayFilter apply(Object config) {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();

            // 不是登录请求，直接向下执行
            if (!StrUtil.containsAnyIgnoreCase(request.getURI().getPath(), SecurityConstants.OAUTH_TOKEN_URL)) {
                return chain.filter(exchange);
            }

            URI uri = exchange.getRequest().getURI();
            String queryParam = uri.getRawQuery();
            Map<String, String> paramMap = HttpUtil.decodeParamMap(queryParam, CharsetUtil.CHARSET_UTF_8);
            //解析密码
            String password = paramMap.get(PASSWORD);
            log.info("密码======>" + password.replaceAll(" ", "+"));
            if (StrUtil.isNotBlank(password)) {
                String inputDecryptData = "";
                try {
                    String privateKey = redisUtil.get(Constant.RSA_PRIVATE_KEY).toString();
                    log.info("密钥======>" + privateKey);

                    inputDecryptData = RSAUtils.decrypt(password.replaceAll(" ", "+"), RSAUtils.getPrivateKey(privateKey));
                } catch (Exception e) {
                    log.error("RSA加解密出现异常======>", e);
                    throw new BizException("RSA加解密出现异常");
                }

                try {
                    password = inputDecryptData;
                } catch (Exception e) {
                    log.error("密码解密失败:{}", password);
                    return Mono.error(e);
                }
                paramMap.put(PASSWORD, password.trim());
            }
            //解析QRCode
            String QRCode = paramMap.get(QRCODE);
            if (StrUtil.isNotBlank(QRCode)) {
                String inputDecryptData = "";

                try {
                    String privateKey = redisUtil.get(Constant.RSA_PRIVATE_KEY).toString();
                    log.info("密钥======>" + privateKey);

                    inputDecryptData = RSAUtils.decrypt(QRCode, RSAUtils.getPrivateKey(privateKey));

                    QRCode = inputDecryptData;
                } catch (Exception e) {
                    log.error("QRCode解密失败:{}", QRCode);
                    return Mono.error(e);
                }
                paramMap.put(QRCODE, QRCode.trim());
            }

            URI newUri = UriComponentsBuilder.fromUri(uri).replaceQuery(HttpUtil.toParams(paramMap)).build(true)
                    .toUri();

            ServerHttpRequest newRequest = exchange.getRequest().mutate().uri(newUri).build();
            log.info("登录后的 request：{}", newRequest);
            return chain.filter(exchange.mutate().request(newRequest).build());
        };
    }

    public static void main(String[] args) {
        System.out.println(encrypt("Cyj123456", "thanks,dreamChardonnay"));
    }

}
